/* 
 * BookingController.java 26.12.2011
 *  
 *  
 * Candidate: Alessandra Otterbach
 * Prometric ID: Algisina1
 * Username: Algisina
 *  
 * Sun Certified Developer for Java 2 Platform, Standard Edition Programming 
 * Assignment (CX-310-252A)
 *  
 * This class is part of the Programming Assignment of the Sun Certified 
 * Developer for Java 2 Platform, Standard Edition certification program, must 
 * not be used out of this context and must be used exclusively by Sun 
 * Microsystems, Inc. 
 */
package suncertify.ps;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import suncertify.bs.BusinessServiceImpl;
import suncertify.bs.RemoteBusinessService;
import suncertify.dao.EnumSearchCriteria;
import suncertify.dao.RoomRecord;
import suncertify.db.DataException;
import suncertify.db.RecordAlreadyBookedException;
import suncertify.db.RecordNotFoundException;
import suncertify.db.SecurityException;
import suncertify.util.StringUtils;

/**
 * Controller for all the activity between BookingView and BookingTableModel.
 * Has the book, search and refresh logic.
 * 
 * @author Alessandra Otterbach
 * 
 */
public class BookingController {

    private BookingView view;
    private ApplicationRunMode mode;
    private String port;
    private String ip;
    private String path;

    private BookingTableModel tm;

    /**
     * Constructor with ip, port, mode. Calls the init-method.
     * 
     * @param ip
     * @param port
     * @param mode
     */
    public BookingController(String ip, String port, ApplicationRunMode mode) {
	this.view = new BookingView();
	this.mode = mode;
	this.port = port;
	this.ip = ip;

	init();
    }

    /**
     * Constructor with path, mode. Calls the init-method.
     * 
     * @param path
     * @param mode
     */
    public BookingController(String path, ApplicationRunMode mode) {
	this.view = new BookingView();
	this.mode = mode;
	this.path = path;

	init();
    }
    
    /**
     * Initial work.
     */
    private void init() {
	initBusinessServices();
	initDataInTable();
	view.createAndShowGUI();
	addListener();
    }

    /**
     * Let the view know everything about the listeners, so that the view can
     * communicate with the controller
     * 
     */
    private void addListener() {
	this.view.setBookListener(new BookListener());
	this.view.setSearchListener(new SearchListener());
	this.view.setRefreshListener(new RefreshListener());
	this.view.setListSelectionListener(new ListListener());
    }

    /**
     * Initialize the business services. RemoteBusinessService for network
     * client mode and BusinessService for standalone mode.
     */
    private void initBusinessServices() {
	if (ApplicationRunMode.NETWORK_CLIENT.equals(mode)) {
	    try {
		Registry registry = LocateRegistry.getRegistry(this.ip,
			Integer.parseInt(this.port));

		view.setBusinessService((RemoteBusinessService) registry
			.lookup("RemoteBusinessService"));
	    } catch (RemoteException re) {
		GuiExceptionHandler
			.handleGuiException("Network error. Please check Portnumber and/or IP and see, if server is running. Systemmessage: "
				+ re.getMessage());
		System.exit(0);
	    } catch (NotBoundException nbe) {
		GuiExceptionHandler
			.handleGuiException("Network error. Please check Portnumber and/or IP and see, if server is running. Systemmessage: "
				+ nbe.getMessage());
		System.exit(0);
	    }
	} else if (ApplicationRunMode.STANDALONE_CLIENT.equals(this.mode)) {
	    try {
		view.setBusinessService(new BusinessServiceImpl(this.path));
	    } catch (IOException ioe) {
		GuiExceptionHandler
			.handleGuiException("Error during file processing. Systemmessage: "
				+ ioe.getMessage());
		System.exit(0);
	    }
	}
    }

    /**
     * Inits the initial data in the table. Searches for every record.
     */
    private void initDataInTable() {
	List<RoomRecord> data = new ArrayList<RoomRecord>();
	List<String> column = new ArrayList<String>();
	column.add("Hotel Name");
	column.add("Hotel Location");
	column.add("Size");
	column.add("Smoking");
	column.add("Rate");
	column.add("Date");
	column.add("Customer Id");

	try {
	    data = view.getBusinessService().searchRoom(null);
	} catch (RecordNotFoundException rnf) {
	    GuiExceptionHandler
		    .handleGuiException("Record not found. Systemmessage: "
			    + rnf.getMessage());
	    System.exit(0);
	} catch (IOException ioe) {
	    GuiExceptionHandler
		    .handleGuiException("Error during file processing. Systemmessage: "
			    + ioe.getMessage());
	    System.exit(0);
	}
	tm = new BookingTableModel(data, column);

	JTable table = new JTable(tm);
	view.setTable(table);
    }

    /**
     * Listener to do the booking. Does some checks on the user input. 
     * Max. 8 digits are allowed.
     * Calls the booking method in the business service.
     * Informs the view about changed data in the table.
     * 
     */
    class BookListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    int selectedRow = view.getTable().getSelectedRow();

	    RoomRecord room = tm.getRoomAtSelectedRow(selectedRow);
	    if (room != null) {

		// Already booked
		if (checkCustomerAlreadyBooked(room)) {
		    GuiExceptionHandler
			    .handleGuiException("Room already booked. Please refresh and try again.");
		    return;
		}

		String showInputDialog = JOptionPane
			.showInputDialog("Please enter your customer id");

		if (showInputDialog == null) {
		    // Cancel
		    return;
		}

		if (checkCustomerEmpty(showInputDialog)) {
		    GuiExceptionHandler
			    .handleGuiException("Please enter a customer id.");
		    return;
		}
		
		if (!checkCustomerDigits(showInputDialog)) {
		    GuiExceptionHandler
			    .handleGuiException("Only numbers allowed.");
		    return;
		}

		if (checkCustomerMax(showInputDialog)) {

		    GuiExceptionHandler
			    .handleGuiException("Max 8 characters allowed.");
		    return;
		}

		room.setOwner(showInputDialog);
		try {
		    view.getBusinessService().bookRoom(room.getId(), room);
		    // Inform tablemodel about change
		    tm.fireTableDataChanged();
		} catch (RecordNotFoundException rnf) {
		    GuiExceptionHandler
			    .handleGuiException("Record not found. Systemmessage: "
				    + rnf.getMessage());
		} catch (SecurityException e1) {
		    GuiExceptionHandler
			    .handleGuiException("Security Failure. Record is already locked. Please refresh and try again.");
		} catch (RemoteException re) {
		    GuiExceptionHandler
			    .handleGuiException("Network problem. Systemmessage: "
				    + re.getMessage());
		} catch (RecordAlreadyBookedException rabe) {
		    GuiExceptionHandler
			    .handleGuiException("Room already booked. Please refresh and try again.");
		} catch (DataException dexc) {
		    GuiExceptionHandler.handleGuiException(dexc.getMessage());
		}

	    }
	}
    }

    /**
     * Listener to do the search. 
     * Calls the search-method in the business service and informs the view about changed data.
     * 
     */
    class SearchListener implements ActionListener {

	public void actionPerformed(ActionEvent e) {

	    try {
		Map<EnumSearchCriteria, String> searchCriterias = new HashMap<EnumSearchCriteria, String>();

		searchCriterias.put(EnumSearchCriteria.NAME, view.getTxtName()
			.getText());
		searchCriterias.put(EnumSearchCriteria.LOCATION, view
			.getTxtLocation().getText());

		List<RoomRecord> roomList = view.getBusinessService()
			.searchRoom(searchCriterias);
		tm.roomList = roomList;
		// Inform tablemodel about change
		tm.fireTableDataChanged();
	    } catch (RecordNotFoundException rnf) {
		GuiExceptionHandler
			.handleGuiException("Record not found. Systemmessage: "
				+ rnf.getMessage());
	    } catch (DataException de) {
		GuiExceptionHandler.handleGuiException(de.getMessage());
	    } catch (RemoteException re) {
		GuiExceptionHandler
			.handleGuiException("Network problem. Systemmessage: "
				+ re.getMessage());
	    }
	}

    }

    /**
     * Listener to do the refresh. Empties the text fields and informs the view about changed data in the table.
     * 
     */
    class RefreshListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {

	    try {
		List<RoomRecord> roomList = view.getBusinessService()
			.searchRoom(null);
		tm.roomList = roomList;
		view.getTxtName().setText("");
		view.getTxtLocation().setText("");
		tm.fireTableDataChanged();
	    } catch (RecordNotFoundException rnf) {
		GuiExceptionHandler
			.handleGuiException("Record not found. Systemmessage: "
				+ rnf.getMessage());
	    } catch (DataException de) {
		GuiExceptionHandler.handleGuiException(de.getMessage());
	    } catch (RemoteException re) {
		GuiExceptionHandler
			.handleGuiException("Network problem. Systemmessage: "
				+ re.getMessage());
	    }
	}

    }

    /**
     * Listener for the listSelection.
     * 
     */
    class ListListener implements ListSelectionListener {

	@Override
	public void valueChanged(ListSelectionEvent e) {
	    // Activate bbok-button only if a row is selected.
	    view.enableBookButton(true);
	}
    }

    /**
     * Checks if the input of the customer id is more than 8 length.
     * 
     * @param customer
     * @return true, if more than 8 characters.
     */
    private boolean checkCustomerMax(String customer) {
	if (customer.length() > 8) {
	    return true;
	}

	return false;
    }

    /**
     * Checks if the input of the customer id is empty.
     * 
     * @param customer
     * @return true, if empty
     */
    private boolean checkCustomerEmpty(String customer) {
	if (StringUtils.isNotEmpty(customer)) {
	    return false;
	}

	return true;
    }

    /**
     * Checks if the input of the customer id is only digits.
     * 
     * @param customer
     * @return true, if only digits
     */
    private boolean checkCustomerDigits(String customer) {
	return customer.matches("\\d*");
    }

    /**
     * Checks if the record is already booked.
     * 
     * @param roomRecord
     * @return true, if already booked
     */
    private boolean checkCustomerAlreadyBooked(RoomRecord roomRecord) {
	if (StringUtils.isEmpty(roomRecord.getOwner())) {
	    return false;
	}

	return true;
    }

}
